#! /usr/bin/env perl

# Run and get the stats for a batch of partitions

# For the wrekagrid project,
# Basile Clout, November 2007

use Getopt::Long;
use Statistics::Descriptive;

use strict;
use warnings;

sub usage {
  print <<EOF;

Usage: evalParts.pl [-f file containing the list of partitions] [-n
number of kambiz runs per partition][-h help] [-u user] [-l logfile]
[-r .res file] <.node file> <.ele file> [<list of partition file>]

Give at least one partition
file either at the end of the command line (prioritary) or in the
partitions file.

Run kambiz's bench -n times on each given partitions.

EOF
exit;
}

  
my @fparts_l=();
my $fparts="";
my ($fnode, $fele)=("","");
my $logfile = "evalParts.log";
my $resfile = "evalParts.res";
my $help;
my $user="root";

my $run_bin = "./runKambiz.pl";
my $tests = 3;			# Default number of tests per partition
my $tests_log = "runKambiz.log"; # Log file of the called program (runKambiz.pl)
my $tests_res = "runKambiz.res";

GetOptions("n=i"=>\$tests, "u=s"=>\$user, "h"=>\$help, "l=s"=>\$logfile, "r=s"=>\$resfile, "f=s"=>\$fparts);

if ( ($#ARGV < 2 && $fparts eq "") || $help){
  usage;
}

$fnode=$ARGV[0];
$fele=$ARGV[1];
if (! -r $fnode){
  die("Node file \"$fnode\" is not readable");
}

if (! -r $fele){
  die("Element file \"$fele\" is not readable");
}

# usage file
open LOG, ">$logfile" or die "Impossible to create $logfile: $!";
open RES, ">$resfile" or die "Impossible to create $resfile: $!";
my ($sec, $min, $hrs, $day, $month, $year) = (localtime) [0,1,2,3,4,5];
my $date = sprintf("%04d-%02d-%02d %d:%d:%d\n", $year+1900, $month+1, $day, $hrs, $min, $sec);
print LOG "# Get stats for a bench of partitions, Basile Clout, $date \n\n";
print RES "# Get stats for a bench of partitions, Basile Clout, $date \n\n";


if ($#ARGV>1){
  @fparts_l=@ARGV[2..$#ARGV];
}elsif ($fparts ne ""){
  open(FPARTS, $fparts) or die("Impossible to open partitions file $fparts: $!");
  @fparts_l=<FPARTS>;
  close(FPARTS);
}
print LOG @fparts_l;
close(LOG);

my ($order, @content, $line, $logline);
my (@val_arr, @val_line);
my ($c, $p)=(0,0);
my $fpart;

# First control that all the part files given are readable
for $fpart (@fparts_l){
  chomp $fpart;
  die("Partition file \"$fpart\" impossible to read!") if ( ! -r $fpart);
}

# Loop over all the files
foreach $fpart (@fparts_l){
    
    chomp $fpart;
    $logline  ="#\@$fpart (".eval($c+1)."/".eval($#fparts_l+1).")...\n";
    print $logline;
    print RES "\n\n$logline";
    system("echo \"\n\n$logline\n\" >> $logfile");
    
    $p=0;
    
    $order = "$run_bin -n $tests -u $user -l $tests_log $fnode $fele $fpart ";
    system("echo \"\n\nOrder: \n$order\" >> $logfile");
    open(FPART, "$order |");
    @content = <FPART>;
    $logline = "\n\nTest part $fpart: \n".join(' ', @content)."\n";
    system("echo \"$logline\" >> $logfile");
    system("echo \"$fpart log file: \n\" >> $logfile");
    addlog($logfile, $tests_log); 
    system("echo \"\n\n$fpart res file: \n\" >> $logfile");
    addlog($logfile, $tests_res);
    
    # Parse output
    my $string="";
    foreach $line (@content){
      if ($line !~ /^[#@]/){
	chomp $line;
	@val_line = split ' ', $line;
	$val_arr[$c][$val_line[0]]=[$val_line[1], $val_line[2], $val_line[3]];
	$p++;
      }
      if($line =~ /^@/){
	$string += $line;
      }
    }
    
    for (my $i=0;$i<$p;$i++){
      print RES join(' ', @{$val_arr[$c][$i]}), "\n";
      print LOG join(' ', @{$val_arr[$c][$i]}), "\n";
    }
    print RES $string;
    print LOG $string;

    $c++;
  }

# Compute stats
my ($string, $stringlog) = &print_stats(\@val_arr, $p, $c, $tests);
print RES $stringlog;
system("echo \"$stringlog\" >> $logfile");

print $string;
  
sub print_stats($$$$) {

  my ($val_arr, $p, $c, $n) = ($_[0], $_[1], $_[2], $_[3]);
  my @val_arr=@{$val_arr};
  my ($string, $stringlog) = ("","");
  
  my $stat;
  my @avg_times_arr;
  my @avg_comm_arr;
  my @avg_cg_arr;
  
  for my $part (0 .. $p-1) {
    
    my @cmp_arr;
    my @com_arr;
    my @cg_arr;

    for my $test (0 .. $c-1) {
      $cmp_arr[$test] = $val_arr[$test][$part][0];
      $com_arr[$test] = $val_arr[$test][$part][1];
      $cg_arr[$test] = $val_arr[$test][$part][2];
    }

    $stat = Statistics::Descriptive::Sparse->new();
    $stat->add_data(@cmp_arr);
    my $cmp_min = $stat->min();
    my $cmp_max = $stat->max();
    my $cmp_avg = $stat->sum()/$stat->count();
    push @avg_times_arr, $cmp_avg;
    my $cmp_std=0;
    if ($c > 1){
      $cmp_std = $stat->standard_deviation();
    }else{
      $cmp_std = 0;
    }
    
    $stat = Statistics::Descriptive::Sparse->new();
    $stat->add_data(@com_arr);
    my $com_min = $stat->min();
    my $com_max = $stat->max();
    my $com_avg = $stat->sum()/$stat->count();
    push @avg_comm_arr, $com_avg;
    my $com_std=0;
    if ($c > 1){
      $com_std = $stat->standard_deviation();
    }else{
      $com_std = 0;
    }
    
    $stat = Statistics::Descriptive::Sparse->new();
    $stat->add_data(@cg_arr);
    my $cg_min = $stat->min();
    my $cg_max = $stat->max();
    my $cg_avg = $stat->sum()/$stat->count();
    push @avg_cg_arr, $cg_avg;
    my $cg_std=0;
    if ($c > 1){
      $cg_std = $stat->standard_deviation();
    }else{
      $cg_std = 0;
    }

    $string .= sprintf "%d  %.2f  %.5f  %d\n", $part, $cmp_avg, $com_avg, $cg_avg;
    $stringlog .= sprintf "%d %.2f(%.2f %.2f %.3f) %.5f(%.5f %.5f %.3f) %d(%d %d %.3f)\n",$part, $cmp_avg, $cmp_min, $cmp_max, $cmp_std, $com_avg, $com_min, $com_max, $com_std, $cg_avg, $cg_min, $cg_max, $cg_std;
  }
  
  $stat = Statistics::Descriptive::Sparse->new();
  $stat->add_data(@avg_times_arr);
  my $max_avg_time = $stat->max();
  my $min_avg_time = $stat->min();
  my $std_avg_time = 0;
  if($p>1){
    $std_avg_time = $stat->standard_deviation();
  }

  $stat = Statistics::Descriptive::Sparse->new();
  $stat->add_data(@avg_comm_arr);
  my $max_avg_comm = $stat->max();
  my $min_avg_comm = $stat->min();
  my $std_avg_comm = 0;
  if ($p>1){
    $std_avg_comm = $stat->standard_deviation();
  }
  
  $stat = Statistics::Descriptive::Sparse->new();
  $stat->add_data(@avg_cg_arr);
  my $max_avg_cg = $stat->max();
  my $min_avg_cg = $stat->min();
  my $std_avg_cg = 0;
  if($p>1){
    $std_avg_cg = $stat->standard_deviation();
  }
  
  my $nb_line = "\@tests $c $n\n";
  my $time_line = sprintf("\@time %.2f %.2f %.6f\n", $min_avg_time, $max_avg_time, $std_avg_time);
  my $comm_line = sprintf("\@comm %.5f %.5f %.6f\n", $min_avg_comm, $max_avg_comm, $std_avg_comm);
  my $cg_line = sprintf("\@cg %d %d %.2f\n", $min_avg_cg, $max_avg_cg, $std_avg_cg);
  
  
  $string .= "$nb_line$time_line$comm_line$cg_line";
  $stringlog .= "$nb_line$time_line$comm_line$cg_line";

  return ($string, $stringlog);
}

sub addlog {
    my ($logfile, $file) = ($_[0], $_[1]);
    system("cat $logfile $file > evalPart.tmp; mv evalPart.tmp $logfile; rm -f evalPart.tmp");
}
